ReactJS 参考

Author: ChinSyun Pang
Weibo: arthinking_plus
Posted in: http://www.itzhai.com

1、顶层API

1.1、React

React 是 React 库的入口。如果使用的是预编译包,则 React 是全局的;如果使用 CommonJS 模块系统,则可以用 require() 函数引入 React。

React.createClass

创建一个组件类,并作出定义。组件实现了 render() 方法,该方法返回一个子级。该子级可能包含很深的子级结构。

React.createElement

创建并返回一个新的指定类型的 ReactElement。

React.createFactory

返回一个生成指定类型 ReactElements 的函数。

React.render

渲染一个 ReactElement 到 DOM 中。

React.unmountComponentAtNode

从 DOM 中移除已经挂载的 React 组件,清除相应的事件处理器和 state。

React.renderToString

把组件渲染成原始的 HTML 字符串。该方法应该仅在服务器端使用。

React.renderToStaticMarkup

和 renderToString 类似,除了不创建额外的 DOM 属性,例如 data-react-id,因为这些属性仅在 React 内部使用。如果你想用 React 做一个简单的静态页面生成器,这是很有用的,因为丢掉额外的属性能够节省很多字节。

React.isValidElement

判断对象是否是一个 ReactElement。

React.DOM

React.DOM 运用 React.createElement 为 DOM 组件提供了方便的包装。该方式仅在未使用 JSX 的时候适用。例如,React.DOM.div(null, ‘Hello World!’)。

React.PropTypes

React.PropTypes 包含了能与组件 propTypes 对象共用的类型,用于验证传入组件的 props。

React.initializeTouchEvents

配置 React 的事件系统,使 React 能处理移动设备的触摸( touch )事件。

React.Children

React.Children 为处理 this.props.children 这个封闭的数据结构提供了有用的工具。

React.Children.map

在每一个直接子级(包含在 children 参数中的)上调用 fn 函数,此函数中的 this 指向 上下文。如果 children 是一个内嵌的对象或者数组,它将被遍历:不会传入容器对象到 fn 中。

React.Children.forEach

类似于 React.Children.map(),但是不返回对象。

React.Children.count

返回 children 当中的组件总数,和传递给 map 或者 forEach 的回调函数的调用次数一致。

React.Children.only

返回 children 中仅有的子级。否则抛出异常。

2、组件API

ReactComponent。唯一一种在 React 之外获取 React 组件实例句柄的方式就是保存 React.render 的返回值。在其它组件内,可以使用 refs 得到相同的结果。

2.1、setState

合并 nextState 和当前 state。这是在事件处理函数中和请求回调函数中触发 UI 更新的主要方法。

绝对不要直接改变 this.state;
setState() 不会立刻改变 this.state,而是创建一个即将处理的 state 转变;
不保证 setState() 调用的同步性;
setState() 将总是触发一次重绘,除非在 shouldComponentUpdate() 中实现了条件渲染逻辑。

2.2、replaceState

类似于 setState(),但是删除之前所有已存在的 state 键,这些键都不在 nextState 中。

2.3、forceUpdate()

如果 render() 方法从 this.props 或者 this.state 之外的地方读取数据,你需要通过调用 forceUpdate() 告诉 React 什么时候需要再次运行 render()。如果直接改变了 this.state,也需要调用 forceUpdate()。

通常情况下,应该尽量避免所有使用forceUpdate()的情况。

2.4、getDOMNode

如果组件已经挂载到了 DOM 上,该方法返回相应的本地浏览器 DOM 元素。

2.5、iSMounted()

如果组件渲染到了 DOM 中,isMounted() 返回 true。

2.6、setProps()

可以调用 setProps() 来改变组件的属性,触发一次重新渲染。

刚方法仅在根组件上面调用。也就是说,仅在直接传给 React.render() 的组件上可用,在它的子级组件上不可用。如果你倾向于在子组件上使用 setProps(),不要利用响应式更新,而是当子组件在 render() 中创建的时候传入新的 prop 到子组件中。

2.7、replaceProps

类似于 setProps(),但是删除所有已存在的 props,而不是合并新旧两个 props 对象。

3、组件的详细说明和生命周期

3.1、组件的详细说明

render

当调用的时候,会检测 this.props 和 this.state,返回一个单子级组件。

render() 函数应该是纯粹的,也就是说该函数不修改组件 state,每次调用都返回相同的结果,不读写 DOM 信息,也不和浏览器交互(例如通过使用 setTimeout)。

getInitialState

在组件挂载之前调用一次。返回值将会作为 this.state 的初始值。

getDefaultProps

在组件类创建的时候调用一次,然后返回值被缓存下来。如果父组件没有指定 props 中的某个键,则此处返回的对象中的相应属性将会合并到 this.props (使用 in 检测属性)。

propTypes

propTypes 对象允许验证传入到组件的 props。

mixins

mixin 数组允许使用混合来在多个组件之间共享行为。

statics

statics 对象允许你定义静态的方法,这些静态的方法可以在组件类上调用。

displayName

displayName 字符串用于输出调试信息。

3.2、组件的生命周期

挂载:componentWillMount

服务器端和客户端都只调用一次,在初始化渲染执行之前立刻调用。如果在这个方法内调用 setState,render() 将会感知到更新后的 state,将会执行仅一次,尽管 state 改变了。

挂载:componentDidMount

在初始化渲染执行之后立刻调用一次,仅客户端有效(服务器端不会调用)。在生命周期中的这个时间点,组件拥有一个 DOM 展现,你可以通过 this.getDOMNode() 来获取相应 DOM 节点。

如果想和其它 JavaScript 框架集成,使用 setTimeout 或者 setInterval 来设置定时器,或者发送 AJAX 请求,可以在该方法中执行这些操作。

更新:componentWillReceiveProps

在组件接收到新的 props 的时候调用。在初始化渲染的时候,该方法不会调用。

用此函数可以作为 react 在 prop 传入之后, render() 渲染之前更新 state 的机会。老的 props 可以通过 this.props 获取到。在该函数中调用 this.setState() 将不会引起第二次渲染。

更新:shouldComponentUpdate

在接收到新的 props 或者 state,将要渲染之前调用。该方法在初始化渲染的时候不会调用,在使用 forceUpdate 方法的时候也不会。

如果确定新的 props 和 state 不会导致组件更新,则此处应该 返回 false。

更新:componentWillUpdate

在接收到新的 props 或者 state 之前立刻调用。在初始化渲染的时候该方法不会被调用。

使用该方法做一些更新之前的准备工作。

你不能在刚方法中使用 this.setState()。如果需要更新 state 来响应某个 prop 的改变,请使用 componentWillReceiveProps。

更新:componentDidUpdate

在组件的更新已经同步到 DOM 中之后立刻被调用。该方法不会在初始化渲染的时候调用。

使用该方法可以在组件更新之后操作 DOM 元素。

移除:componentWillUnmount

在组件从 DOM 中移除的时候立刻被调用。

在该方法中执行任何必要的清理,比如无效的定时器,或者清除在 componentDidMount 中创建的 DOM 元素。

3、标签和属性支持

3.1、支持的标签

React 尝试支持所用常用的元素。如果你需要的元素没有在下面列出来,请提交一个问题(issue)。

HTML元素

SVG元素

3.2、支持度 属性

React 支持所有 data- 和 aria- 属性,也支持下面列出的属性。

所有的属性都是驼峰命名的,class 属性和 for 属性分别改为 className 和 htmlFor,来符合 DOM API 规范。

HTML属性

1
2
3
4
5
6
7
8
9
10
11
accept acceptCharset accessKey action allowFullScreen allowTransparency alt
async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
className cols colSpan content contentEditable contextMenu controls coords
crossOrigin data dateTime defer dir disabled download draggable encType form
formAction formEncType formMethod formNoValidate formTarget frameBorder height
hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
marginHeight marginWidth max maxLength media mediaGroup method min multiple
muted name noValidate open pattern placeholder poster preload radioGroup
readOnly rel required role rows rowSpan sandbox scope scrolling seamless
selected shape size sizes span spellCheck src srcDoc srcSet start step style
tabIndex target title type useMap value width wmode

SVG属性

1
2
3
4
5
cx cy d dx dy fill fillOpacity fontFamily fontSize fx fy gradientTransform
gradientUnits markerEnd markerMid markerStart offset opacity
patternContentUnits patternUnits points preserveAspectRatio r rx ry
spreadMethod stopColor stopOpacity stroke strokeDasharray strokeLinecap
strokeOpacity strokeWidth textAnchor transform version viewBox x1 x2 x y1 y2 y

4、事件系统

4.1、虚拟事件对象

事件处理器将会传入虚拟事件对象的实例,一个对浏览器本地事件的跨浏览器封装。它有和浏览器本地事件相同的属性和方法,包括 stopPropagation() 和 preventDefault(),但是没有浏览器兼容问题。

如果因为一些因素,需要底层的浏览器事件对象,只要使用nativeEvent属性就可以获取到它了。每一个虚拟事件对象都有下列的属性:

1
2
3
4
5
6
7
8
9
10
11
12
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
void stopPropagation()
DOMEventTarget target
number timeStamp
string type

4.2、支持的事件

React 标准化了事件对象,因此在不同的浏览器中都会有相同的属性。

如下的事件处理器在事件冒泡阶段触发。要在捕获阶段触发某个事件处理器,在事件名字后面追加 Capture字符串;例如,使用 onClickCapture

剪贴板事件

onCopy onCut onPaste

键盘事件

onKeyDown onKeyPress onKeyUp

焦点事件

onFocus onBlur

表单事件

onChange onInput onSubmit

鼠标事件

onClick onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave
onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave
onMouseMove onMouseOut onMouseOver onMouseUp

触摸事件

onTouchCancel onTouchEnd onTouchMove onTouchStart

UI事件

onScroll

鼠标滚轮滚动事件

onWheel

5、与DOM的差异

React 为了性能和跨浏览器的原因,实现了一个独立于浏览器的事件和 DOM 系统。利用此功能,可以屏蔽掉一些浏览器的 DOM 的粗糙实现。

6、特殊的非DOM属性

除了与 DOM 的差异之外,React 也提供了一些 DOM 里面不存在的属性:

  • key
  • ref
  • dangerouslySetInnerHTML

7、Reconciliation

这篇文章解释了我们如何使用强大的试探法来将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题。

7.1、动机

生成最少的将一颗树形结构转换成另一颗树形结构的操作,是一个复杂的,并且值得研究的问题。最优算法的复杂度是 O(n3),n 是树中节点的总数。

7.2、两个节点的差异检查

不同的节点类型

如果节点的类型不同,React 将会把它们当做两个不同的子树,移除之前的那棵子树,然后创建并插入第二棵子树。

DOM节点

当比较两个 DOM 节点的时候,我们查看两者的属性,然后能够找出哪一个属性随着时间产生了变化。

自定义组件

因为组件是状态化的,不可能每次状态改变都要创建一个新的组件实例。React 利用新组件上的所有属性,然后在之前的组件实例上调用 component[Will/Did]ReceiveProps()。

7.2、子级优化差异算法(List-wise diff)

问题点(Problematic Case)

为了完成子级更新,React 选用了一种很原始的方法。React 同时遍历两个子级列表,当发现差异的时候,就产生一次 DOM 修改。

键(Keys)

为了解决这个看起来很棘手的问题,引入了一个可选的属性。可以给每个子级一个键值,用于将来的匹配比较。如果指定了一个键值,React 就能够检测出节点插入、移除和替换,并且借助哈希表使节点移动复杂度为 O(n)。

7.3、权衡(Trade-offs)

为了完成子级更新,React 选用了一种很原始的方法。React 同时遍历两个子级列表,当发现差异的时候,就产生一次 DOM 修改。

8、React(虚拟)DOM术语

五个核心类型:

1
2
3
ReactElement / ReactElement 工厂
ReactNode
ReactComponent / ReactComponent 类

8.1、React元素

React 中最主要的类型就是 ReactElement。它有四个属性:type,props,key 和 ref。它没有方法,并且原型上什么都没有。

可以通过 React.createElement 创建该类型的一个实例。

1
var root = React.createElement('div');

要添加属性到 DOM 元素,把属性对象作为第二个参数传入 React.render,把子级作为第三个参数传给 React.render。

1
2
3
var child = React.createElement('li', null, 'Text Content');
var root = React.createElement('ul', { className: 'my-list' }, child);
React.render(root, document.body);

如果使用 React JSX 语法,这些 ReactElement 实例自动创建。

工厂

一个 ReactElement 工厂就是一个简单的函数,该函数生成一个带有特殊 type 属性的 ReactElement:

1
2
3
function createFactory(type){
return React.createElement.bind(null, type);
}

该函数能创建一个方便的短函数,而不是总调用 React.createElement(‘div’):

1
2
3
var div = React.createFactory('div');
var root = div({ className: 'my-div' });
React.render(root, document.body);

React 已经内置了常用 HTML 标签的工厂函数:

1
2
3
var root = React.DOM.ul({ className: 'my-list' },
React.DOM.li(null, 'Text Content')
);

如果使用 JSX 语法,就不需要工厂函数了。JSX 已经提供了一种方便的短函数来创建 ReactElement 实例。

8.2、React节点

一个 ReactNode 可以是:

  • ReactElement
  • string (又名 ReactText)
  • number (又名 ReactText)
  • ReactNode 实例数组 (又名 ReactFragment)

8.3、React组件

在使用 React 开发中,可以仅使用 ReactElement 实例,但是,要充分利用 React,就要使用 ReactComponent 来封装状态化的组件。

一个 ReactComponent 类就是一个简单的 JavaScript 类(或者说是“构造函数”)。

1
2
3
4
5
var MyComponent = React.createClass({
render: function() {
...
}
});

当该构造函数调用的时候,应该会返回一个对象,该对象至少带有一个 render 方法。该对象指向一个 ReactComponent 实例。

除非为了测试,正常情况下不要自己调用该构造函数。React 帮你调用这个函数。

把 ReactComponent 类传给 createElement,就会得到一个 ReactElement 实例。

1
var element = React.createElement(MyComponent);

或者使用 JSX:

1
var element = <MyComponent />;

当该实例传给 React.render 的时候,React 将会调用构造函数,然后创建并返回一个 ReactComponent。

1
var component = React.render(element, document.body);

如果一直用相同的 ReactElement 类型和相同的 DOM 元素容器调用 React.render,将会总是返回相同的实例。该实例是状态化的。如果自己手动创建Componnet实力,则不会又这样的优化。

ReactComponent 的 render 方法应该返回另一个 ReactElement,这就允许组件被组装。

8.4、正式的类型定义

入口点

节点和元素

类和组件